/*==========================================================================*\
| $Id: ProfilingSecurityManager.java,v 1.1 2007/09/15 01:58:39 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Modified from Mark Petrovic's original:
|
| Copyright (c) 2006 Mark Petrovic <mspetrovic@gmail.com>
|
| Permission is hereby granted, free of charge, to any person obtaining
| a copy of this software and associated documentation files (the
| "Software"), to deal in the Software without restriction, including
| without limitation the rights to use, copy, modify, merge, publish,
| distribute, sublicense, and/or sell copies of the Software, and to
| permit persons to whom the Software is furnished to do so, subject to
| the following conditions:
|
| The above copyright notice and this permission notice shall be
| included in all copies or substantial portions of the Software.
|
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| Original Author: Mark Petrovic <mspetrovic@gmail.com>
\*==========================================================================*/
package net.sf.webcat.plugins.javatddplugin;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.CodeSource;
import java.security.Permission;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Set;
//-------------------------------------------------------------------------
/**
* <code>ProfilingSecurityManager</code> is a Java security manager that
* profiles what resources an application accesses, and in what
* manner--e.g., read, write, etc. It does not enforce a
* security policy, but rather produces a starting point for crafting one.
* <p>
* It extends {@link java.lang.SecurityManager} and overrides the two forms
* of the <code>checkPermission()</code> method. For each call to
* <code>checkPermission()</code>, <code>ProfilingSecurityManager</code> first
* guards against the condition that it itself induced the call to
* <code>checkPermission()</code>, which would result in unterminated
* recursion. If a call to <code>checkPermission()</code> resulted from a
* call outside <code>ProfilingSecurityManager</code>, the current context
* is examined and each class found therein is profiled as needing access to
* the {@link java.security.Permission} in question.
*
* Profiling is manifested as a writing to <code>System.out</code> a "grant"
* rule for each <code>java.security.Permission</code> requested
* on a per <code>CodeBase</code> basis.
*
* The implementation here does some very simple rule caching. If a rule has
* been seen previously, it is not output to System.out. The caching cannot
* prevent a security check, but it can reduce I/O during profiling.
*
* @author Mark S. Petrovic, with modifications by Stephen Edwards
* @version $Id: ProfilingSecurityManager.java,v 1.1 2007/09/15 01:58:39 stedwar2 Exp $
*/
public class ProfilingSecurityManager
extends SecurityManager
{
//~ Constructor ...........................................................
// ----------------------------------------------------------
/**
* Creates a new ProfilingSecurityManager object.
*/
public ProfilingSecurityManager()
{
thisClassName=this.getClass().getName();
CodeSource thisCodeSource =
this.getClass().getProtectionDomain().getCodeSource();
thisCodeSourceURLString = thisCodeSource.getLocation().toString();
}
//~ Public Methods ........................................................
// ----------------------------------------------------------
@Override
public void checkPermission(final Permission permission)
{
try
{
super.checkPermission(permission);
}
catch (SecurityException e)
{
final Throwable t = new Throwable("Profiler stack probe");
final StackTraceElement[] stack = t.getStackTrace();
// Avoid recursion owing to actions in this class itself inducing
// callbacks
if( !isRecur(stack) )
{
buildRules(permission, AccessController.getContext());
}
}
}
// ----------------------------------------------------------
@Override
public void checkPermission(
final Permission permission, final Object context)
{
try
{
super.checkPermission(permission, context);
}
catch (SecurityException e)
{
buildRules(permission, (AccessControlContext)context);
}
}
// ----------------------------------------------------------
@Override
public String toString()
{
return "SecurityManager: " + psmMsg;
}
//~ Private Methods .......................................................
// ----------------------------------------------------------
/**
* With a Permission and an AccessControlContext, we can build and print
* rules.
*/
private void buildRules(
final Permission permission, final AccessControlContext ctx)
{
try
{
final ProtectionDomain[] protectionDomain =
getProtectionDomains(ctx);
if (null != protectionDomain)
{
for (int i = 0; i < protectionDomain.length; ++i)
{
final String grant =
formatRule(permission, protectionDomain[i]);
if (null != grant && !isCached(grant))
{
println(grant);
}
}
}
}
catch (IllegalStateException e)
{
e.printStackTrace();
}
}
// ----------------------------------------------------------
/**
* Traverse the stack, returning true if the stack indicates we called
* ourself.
*/
private boolean isRecur(final StackTraceElement[] st)
{
boolean v = false;
for (int i = st.length - 1; i >= 1; --i)
{
final boolean c = st[i].getClassName().equals(thisClassName);
final boolean m = st[i].getMethodName().equals("buildRules");
if (c && m)
{
v = true;
break;
}
}
return v;
}
// ----------------------------------------------------------
/**
* Get the protection domains by Java reflection. There is no public API
* for this info, making this code Sun
* Java 1.5 JVM implementation
* dependent.
*/
private ProtectionDomain[] getProtectionDomains(
final AccessControlContext context)
throws IllegalStateException
{
ProtectionDomain[] pda = null;
try
{
final Field[] fields =
AccessControlContext.class.getDeclaredFields();
if (null == fields)
{
throw new IllegalStateException("No fields");
}
for (int i = 0; i < fields.length; ++i)
{
if (fields[i].getName().equals("context"))
{ // Warning: JVM-dependent
fields[i].setAccessible(true);
final Object o = fields[i].get(context);
pda = (ProtectionDomain[])o;
break;
}
}
// No 'context' field found, throw exception.
if (null == pda)
{
throw new IllegalStateException("No \"context\" Field found!");
}
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
return pda;
}
// ----------------------------------------------------------
private String formatRule(
final Permission permission, final ProtectionDomain pd)
{
final CodeSource cs = pd.getCodeSource();
if (null == cs)
{
return null;
}
final URL url = cs.getLocation();
if (null == url)
{
return null;
}
// Remove ProfilingSecurityManager.class codebase from output rule
// consideration
if (url.toString().equals(thisCodeSourceURLString))
{
return null;
}
final StringBuilder sb = new StringBuilder();
sb.append("grant codeBase \"");
sb.append(url.toString());
sb.append("\" {");
sb.append("permission ");
sb.append(" ");
sb.append(permission.getClass().getName());
sb.append(" ");
sb.append("\"");
// Some complex permissions have quoted strings embedded or
// literal carriage returns that must be escaped.
final String permissionName = permission.getName();
final String escapedPermissionName =
permissionName.replace("\"","\\\"").replace("\r","\\\r");
sb.append(escapedPermissionName);
sb.append("\", ");
sb.append("\"");
sb.append(permission.getActions());
sb.append("\";");
sb.append("};");
return sb.toString();
}
// ----------------------------------------------------------
/**
* If the rule has been seen during this runtime invocation, do not
* print it again.
*/
private boolean isCached(final String candidate)
{
synchronized (cache)
{
if (cache.contains(candidate))
{
return true;
}
else
{
cache.add(candidate);
return false;
}
}
}
// ----------------------------------------------------------
private void println(final String value)
{
String outFileName =
System.getProperty("ProfilingSecurityManager.output");
if (outFileName != null)
{
try
{
PrintStream out = new PrintStream(
new FileOutputStream(new File(outFileName), true));
out.println(value);
out.close();
}
catch (IOException e)
{
System.out.println(e);
}
}
System.out.println(value);
}
//~ Instance/static variables .............................................
/* Variables of pure convenience */
final private String thisClassName;
final private String thisCodeSourceURLString;
final private String psmMsg = "ProfilingSecurityManager";
final private Set<String> cache = new HashSet<String>();
}